// 引入初始化上传的方法
import { initUpload } from './initUpload'

/**
 * 清理字符串 - 去除空格和换行
 * @param {string} str - 需要清理的字符串
 * @returns {string} 清理后的字符串
 */
function cleanString(str) {
    if (!str || typeof str !== 'string') return '';
    // 移除所有空格、换行符、制表符等空白字符
    return str.replace(/\s+/g, '');
}

/**
 * 全局错误防护 - 包装任何可能导致索引越界的函数调用
 * 所有涉及二进制数据处理的代码都应该通过此函数包装
 */
function safeBinaryOperation(operation, fallbackValue = null) {
    try {
        return operation();
    } catch (error) {
        console.error('二进制数据操作出错:', error.message);
        // 返回安全的默认值而不是抛出错误
        return fallbackValue;
    }
}

/**
 * 全局的安全访问方法 - 返回安全的字符串
 * @param {any} str - 需要安全访问的字符串
 * @returns {string} 安全的非空字符串
 */
function safeString(str) {
    if (str === null || str === undefined) {
        return '';
    }
    
    if (typeof str !== 'string') {
        try {
            return String(str);
        } catch (e) {
            return '';
        }
    }
    
    return str;
}

/**
 * 全局的安全字符代码访问方法
 * @param {string} str - 字符串
 * @param {number} index - 索引
 * @returns {number} 字符代码或0
 */
function safeCharCodeAt(str, index) {
    return safeBinaryOperation(() => {
        str = safeString(str);
        if (!str || str.length === 0 || index < 0 || index >= str.length) {
            return 0;
        }
        
        const code = str.charCodeAt(index);
        return isNaN(code) ? 0 : code;
    }, 0);
}

/**
 * 安全地创建ArrayBuffer
 * @param {string} base64Data - Base64编码的数据
 * @returns {ArrayBuffer} 安全的ArrayBuffer
 */
function safeCreateArrayBuffer(base64Data) {
    return safeBinaryOperation(() => {
        if (!base64Data || typeof base64Data !== 'string' || base64Data.length === 0) {
            console.warn('创建ArrayBuffer时收到空数据');
            // 返回一个安全的非空ArrayBuffer
            return new ArrayBuffer(1);
        }
        
        // 在微信小程序环境中
        if (typeof wx !== 'undefined' && wx.base64ToArrayBuffer) {
            try {
                const buffer = wx.base64ToArrayBuffer(base64Data);
                if (!buffer || buffer.byteLength === 0) {
                    console.warn('wx.base64ToArrayBuffer返回空数据');
                    return new ArrayBuffer(1);
                }
                return buffer;
            } catch (e) {
                console.error('wx.base64ToArrayBuffer出错:', e);
                return new ArrayBuffer(1);
            }
        }
        
        // 在其他环境中
        try {
            const binaryString = atob(base64Data);
            const len = binaryString.length;
            const bytes = new Uint8Array(len || 1); // 确保至少长度为1
            
            for (let i = 0; i < len; i++) {
                bytes[i] = safeCharCodeAt(binaryString, i);
            }
            
            return bytes.buffer;
        } catch (e) {
            console.error('atob转换出错:', e);
            return new ArrayBuffer(1);
        }
    }, new ArrayBuffer(1));
}

/**
 * 安全地将字符串转换为Uint8Array
 * @param {string} str - 输入字符串
 * @param {string} sourceName - 来源名称（用于日志）
 * @returns {Uint8Array} 转换结果
 */
function safeStringToUint8Array(str, sourceName = '未知') {
    return safeBinaryOperation(() => {
        // 使用安全方法确保我们有有效的字符串
        const safeStr = safeString(str);
        
        if (safeStr.length === 0) {
            console.error(`[${sourceName}] 安全化后的字符串长度为0`);
            return new Uint8Array([0]); // 返回安全的非空数组
        }
        
        // 创建足够大的缓冲区
        const bytes = new Uint8Array(safeStr.length || 1); // 确保至少长度为1
        
        // 逐个字符安全转换
        let validChars = 0;
        for (let i = 0; i < safeStr.length; i++) {
            // 使用安全的字符代码访问方法
            const code = safeCharCodeAt(safeStr, i);
            bytes[i] = code;
            if (code > 0) {
                validChars++;
            }
        }
        
        // 如果没有有效字符，返回一个安全的非空数组
        if (validChars === 0) {
            console.warn(`[${sourceName}] 没有找到有效字符，返回安全的非空数组`);
            return new Uint8Array([0]); // 返回一个包含单个0的数组
        }
        
        return bytes;
    }, new Uint8Array([0])); // 返回安全的默认值
}

/**
 * 验证二进制数据是否有效
 * @param {any} data - 要验证的二进制数据
 * @param {string} sourceName - 数据来源名称（用于日志）
 * @returns {object} 包含是否有效和错误信息的对象
 */
function validateBinaryData(data, sourceName = '未知') {
    return safeBinaryOperation(() => {
        // 检查数据是否存在
        if (data === null || data === undefined) {
            console.error(`[${sourceName}] 二进制数据为空或未定义`);
            return {
                valid: false,
                error: `[${sourceName}] 二进制数据为空或未定义`
            };
        }
        
        // 检查数据类型
        if (typeof data !== 'string' && !(data instanceof ArrayBuffer) && !(data instanceof Uint8Array)) {
            console.error(`[${sourceName}] 二进制数据类型错误: ${typeof data}`);
            return {
                valid: false,
                error: `[${sourceName}] 二进制数据类型错误: ${typeof data}`
            };
        }
        
        // 检查字符串数据长度
        if (typeof data === 'string' && data.length === 0) {
            console.error(`[${sourceName}] 二进制字符串长度为0`);
            return {
                valid: false,
                error: `[${sourceName}] 二进制字符串长度为0`
            };
        }
        
        // 检查二进制数据长度
        if ((data instanceof ArrayBuffer || data instanceof Uint8Array) && data.byteLength === 0) {
            console.error(`[${sourceName}] 二进制缓冲区长度为0`);
            return {
                valid: false,
                error: `[${sourceName}] 二进制缓冲区长度为0`
            };
        }
        
        // 额外的安全检查：尝试访问第一个字节确认数据是否真的可访问
        if (typeof data === 'string') {
            // 使用安全的方法读取第一个字符代码
            const safeData = safeString(data);
            if (safeData.length === 0) {
                console.error(`[${sourceName}] 安全化后的字符串长度为0`);
                return {
                    valid: false,
                    error: `[${sourceName}] 无效的字符串数据`
                };
            }
        } else if (data instanceof ArrayBuffer) {
            // 尝试创建视图并读取第一个字节
            try {
                const view = new Uint8Array(data);
                if (view.length === 0) {
                    console.error(`[${sourceName}] ArrayBuffer无法创建有效视图`);
                    return {
                        valid: false,
                        error: `[${sourceName}] ArrayBuffer无法正常访问`
                    };
                }
            } catch (e) {
                console.error(`[${sourceName}] 创建ArrayBuffer视图出错:`, e);
                return {
                    valid: false,
                    error: `[${sourceName}] 无法访问ArrayBuffer: ${e.message}`
                };
            }
        }
        
        return { valid: true };
    }, { valid: false, error: `[${sourceName}] 数据验证过程出错` });
}

/**
 * 在处理前确保二进制数据是有效的
 * @param {Object} options - 上传选项
 * @returns {boolean} 数据是否有效
 */
function ensureValidChunkData(options) {
    return safeBinaryOperation(() => {
        if (!options) {
            console.error('选项对象为空');
            return false;
        }
        
        // 首先检查options.body是否存在
        if (!options.body) {
            console.error('options.body不存在');
            return false;
        }
        
        // 检查options.body.chunk是否存在
        if (!options.body.chunk) {
            console.error('options.body.chunk不存在');
            return false;
        }
        
        // 使用安全方法处理字符串
        const safeChunk = safeString(options.body.chunk);
        
        // 检查安全化后的字符串长度
        if (safeChunk.length === 0) {
            console.error('安全化后的options.body.chunk长度为0');
            return false;
        }
        
        return true;
    }, false); // 默认返回false表示验证失败
}

/**
 * 安全地从表单数据中移除二进制内容（用于日志）
 * @param {Object} data - 要处理的数据对象
 * @returns {Object} 处理后的安全对象
 */
function getSafeFormData(data) {
    const result = {};
    if (!data || typeof data !== 'object') return result;
    
    for (const key in data) {
        if (key === 'chunk' || key.includes('binary') || key.includes('file')) {
            // 显示二进制数据的前10个字符和长度信息
            if (data[key] && typeof data[key] === 'string') {
                const dataLength = data[key].length;
                let previewChars = '';
                try {
                    // 获取前10个字符的ASCII码
                    previewChars = Array.from(data[key].substring(0, 10))
                        .map(char => char.charCodeAt(0))
                        .join(',');
                } catch (e) {
                    previewChars = '无法解析';
                }
                result[key] = `[二进制数据前10个字符码: ${previewChars}, 总长度: ${dataLength}字节]`;
            } else {
                result[key] = '[非字符串二进制数据]';
            }
        } else {
            result[key] = data[key];
        }
    }
    return result;
}

/**
 * 获取文件的MIME类型
 * @param {string} filePath - 文件路径
 * @param {string} originalFileName - 原始文件名
 * @returns {string} MIME类型
 */
function getMimeType(filePath, originalFileName) {
    if (!filePath && !originalFileName) return 'application/octet-stream';
    
    // 优先使用原始文件名获取扩展名
    if (originalFileName) {
        // 提取扩展名
        let extension = '';
        const lastDotIndex = originalFileName.lastIndexOf('.');
        if (lastDotIndex !== -1) {
            extension = originalFileName.substring(lastDotIndex + 1).toLowerCase();
            
            // 映射扩展名到MIME类型
            const mimeTypes = {
                'pdf': 'application/pdf',
                'doc': 'application/msword',
                'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                'txt': 'text/plain',
                'jpg': 'image/jpeg',
                'jpeg': 'image/jpeg',
                'png': 'image/png'
            };
            
            if (mimeTypes[extension]) {
                return mimeTypes[extension];
            }
        }
    }
    
    // 如果没有原始文件名或无法从中获取扩展名，尝试从文件路径获取
    try {
        // 提取文件名 - 处理Windows和Unix路径
        let fileName = '';
        if (filePath.includes('\\')) {
            // Windows路径
            fileName = filePath.substring(filePath.lastIndexOf('\\') + 1);
        } else if (filePath.includes('/')) {
            // Unix路径
            fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
        } else {
            // 可能是blob或其他格式
            return 'application/octet-stream';
        }
        
        // 提取扩展名
        let extension = '';
        const lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex !== -1) {
            extension = fileName.substring(lastDotIndex + 1).toLowerCase();
        }
        
        // 映射扩展名到MIME类型
        const mimeTypes = {
            'pdf': 'application/pdf',
            'doc': 'application/msword',
            'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'txt': 'text/plain',
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'png': 'image/png'
        };
        
        return mimeTypes[extension] || 'application/octet-stream';
    } catch (e) {
        console.error('获取MIME类型出错:', e);
        return 'application/octet-stream';
    }
}

/**
 * 获取上传请求头
 * @returns {Object} 请求头对象
 */
function getUploadHeaders() {
    try {
        // 获取token
        let token = '';
        if (typeof uni !== 'undefined' && uni.getStorageSync) {
            token = uni.getStorageSync('token');
        } else if (typeof wx !== 'undefined' && wx.getStorageSync) {
            token = wx.getStorageSync('token');
        }
        
        // 基础请求头
        const headers = {
            'Accept': '*/*'
        };
        
        // 如果token存在，添加到请求头
        if (token) {
            headers['Authorization'] = `Bearer ${token}`;
            console.log('添加token到请求头');
        }
        
        return headers;
    } catch (e) {
        console.error('创建请求头出错:', e);
        return { 'Accept': '*/*' }; // 返回最基本的请求头
    }
}

/**
 * 上传文件到服务器
 * @param {string} filePath - 文件路径
 * @param {Object} options - 上传选项
 * @returns {Promise} 上传结果
 */
export const uploadFile = async (filePath, options = {}) => {
    console.log('==== uploadFile 开始执行 ====');
    
    try {
        // 在处理前确保数据有效性
        if (options.body && options.body.chunk) {
            const isValidChunk = safeBinaryOperation(() => {
                // 执行全面的初始检查
                if (!ensureValidChunkData(options)) {
                    console.error('无效的chunk数据，终止上传');
                    throw new Error('无效的二进制数据，无法进行上传');
                }
                
                const validation = validateBinaryData(options.body.chunk, 'options.body.chunk');
                if (!validation.valid) {
                    console.error(validation.error);
                    throw new Error(validation.error);
                }
                
                // 额外安全检查：确保chunk数据可以被安全处理
                const testBytes = safeStringToUint8Array(options.body.chunk, '初始安全检查');
                if (!testBytes || testBytes.length === 0) {
                    console.error('无法将chunk数据转换为有效的二进制数组');
                    throw new Error('无法处理二进制数据');
                }
                
                const safeLength = safeBinaryOperation(() => options.body.chunk.length, 0);
                console.log('二进制数据校验通过，长度:', safeLength, '类型:', typeof options.body.chunk);
                
                return true;
            }, false);
            
            if (!isValidChunk) {
                return Promise.reject(new Error('二进制数据验证失败'));
            }
        }
        
        // 检查是否是微信小程序环境
        const isWxMP = typeof wx !== 'undefined' && wx.uploadFile && typeof wx.canIUse === 'function';
        console.log('是否为微信小程序环境:', isWxMP);
        
        // 专门为微信小程序环境提供的上传处理
        if (isWxMP && options.body && options.body.chunk) {
            console.log('==== 使用微信小程序专用上传方式 ====');
            
            return new Promise(async (resolve, reject) => {
                try {
                    // 获取fileId - 只在第一个切片时初始化
                    let fileId = options.fileId;
                    console.log('接收到的fileId:', fileId, '切片索引:', options.chunkIndex, 'isFirstChunk:', options.isFirstChunk);
                    
                    // 只有当明确是第一个切片时才调用初始化
                    if (options.isFirstChunk === true) {
                        console.log('这是第一个切片，需要初始化');
                        const initParams = {
                            fileId: options.fileId || 'file_' + Date.now(),
                            fileName: options.originalFileName || 'unknown_file',
                            fileSize: options.fileSize || 0,
                            totalChunks: options.totalChunks || 0
                        };
                        
                        console.log('微信小程序上传前调用初始化接口，参数:', initParams);
                        const initResult = await initUpload(initParams);
                        
                        if (!initResult || initResult.code !== 20000) {
                            return reject(new Error('文件初始化失败: ' + (initResult?.msg || '未知错误')));
                        }
                        
                        // 使用初始化返回的fileId
                        fileId = initResult.data?.fileId || initParams.fileId;
                        console.log('初始化成功，获取到fileId:', fileId);
                    } else {
                        console.log('非第一个切片，跳过初始化，使用现有fileId:', fileId);
                    }
                    
                    // 1. 获取二进制数据并进行安全转换
                    const bytes = safeStringToUint8Array(options.body.chunk, '微信小程序上传');
                    if (bytes.length === 0) {
                        return reject(new Error('转换后的二进制数据为空'));
                    }
                    
                    // 2. 创建临时文件
                    const fs = wx.getFileSystemManager();
                    const tempFilePath = `${wx.env.USER_DATA_PATH}/chunk_${Date.now()}.bin`;
                    
                    try {
                        fs.writeFileSync(tempFilePath, bytes.buffer, 'binary');
                        console.log('临时文件创建成功:', tempFilePath);
                    } catch (e) {
                        console.error('创建临时文件失败:', e);
                        return reject(new Error('创建临时文件失败: ' + e.message));
                    }
                    
                    // 3. 使用wx.uploadFile上传文件
                    const token = wx.getStorageSync('token') || '';
                    const formData = {
                        fileId: fileId || '',
                        chunkIndex: String(options.chunkIndex || 0),
                        kbName: cleanString(options.kbName || ''),
                        kbId: options.kbId || '',
                        chunk: options.body.chunk
                    };
                    
                    // 添加最后一个切片的标记
                    if (options.isLastChunk) {
                        formData.isLastChunk = 'true';
                        console.log('这是最后一个切片，添加isLastChunk标记');
                    }
                    
                    console.log('上传请求参数:', getSafeFormData(formData));
                    console.log('当前上传的切片索引:', options.chunkIndex);
                    
                    // 额外添加原始chunk数据的日志
                    if (options.body && options.body.chunk) {
                        const chunkData = options.body.chunk;
                        const chunkLength = chunkData.length;
                        let chunkPreview = '';
                        try {
                            chunkPreview = Array.from(chunkData.substring(0, 20))
                                .map(char => char.charCodeAt(0))
                                .join(',');
                        } catch (e) {
                            chunkPreview = '无法解析';
                        }
                        console.log(`原始chunk数据预览(前20个字符码): ${chunkPreview}, 总长度: ${chunkLength}字节`);
                    }
                    
                    wx.uploadFile({
                        url: 'http://74ac341c.r3.cpolar.top/upload/files',
                        filePath: tempFilePath,
                        name: 'chunk', // 文件字段名
                        formData: formData,
                        header: {
                            'Accept': '*/*',
                            'Authorization': token ? `Bearer ${token}` : ''
                        },
                        success(res) {
                            console.log('微信小程序上传成功');
                            try {
                                // 删除临时文件
                                try {
                                    fs.unlinkSync(tempFilePath);
                                    console.log('临时文件已删除');
                                } catch (e) {
                                    console.error('删除临时文件失败:', e);
                                }
                                
                                // 解析响应
                                const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
                                resolve(data);
                            } catch (e) {
                                console.error('处理响应出错:', e);
                                reject(e);
                            }
                        },
                        fail(err) {
                            console.error('微信小程序上传失败:', err);
                            try {
                                fs.unlinkSync(tempFilePath);
                                console.log('临时文件已删除');
                            } catch (e) {
                                console.error('删除临时文件失败:', e);
                            }
                            reject(err);
                        }
                    });
                } catch (e) {
                    console.error('微信小程序上传处理出错:', e);
                    reject(e);
                }
            });
        }
        
        // 其他环境的上传处理
        const mimeType = getMimeType(filePath, options.originalFileName);
        console.log('文件MIME类型:', mimeType);
        
        // 获取fileId - 只在第一个切片时初始化
        let fileId = options.fileId;
        console.log('接收到的fileId:', fileId, '切片索引:', options.chunkIndex, 'isFirstChunk:', options.isFirstChunk);
        
        // 只有当明确是第一个切片时才调用初始化
        if (options.isFirstChunk === true) {
            console.log('这是第一个切片，需要初始化');
            const initParams = {
                fileId: options.fileId || 'file_' + Date.now(),
                fileName: options.originalFileName || 'unknown_file',
                fileSize: options.fileSize || 0,
                totalChunks: options.totalChunks || 0
            };
            
            console.log('调用初始化接口，参数:', initParams);
            const initResult = await initUpload(initParams);
            
            if (!initResult || initResult.code !== 20000) {
                throw new Error('文件初始化失败: ' + (initResult?.msg || '未知错误'));
            }
            
            // 使用初始化返回的fileId
            fileId = initResult.data?.fileId || initParams.fileId;
            console.log('初始化成功，获取到fileId:', fileId);
        } else {
            console.log('非第一个切片，跳过初始化，使用现有fileId:', fileId);
        }
        
        // 检查是否有base64编码的chunk数据
        if (options.body && options.body.chunk) {
            return new Promise((resolve, reject) => {
                // 如果是H5环境，尝试使用FormData
                if (typeof FormData !== 'undefined') {
                    try {
                        const formData = new FormData();
                        formData.append('fileId', fileId);
                        formData.append('chunkIndex', String(options.chunkIndex || 0));
                        formData.append('kbName', cleanString(options.kbName || ''));
                        formData.append('kbId', options.kbId || '');
                        
                        // 添加最后一个切片的标记
                        if (options.isLastChunk) {
                            formData.append('isLastChunk', 'true');
                            console.log('这是最后一个切片，添加isLastChunk标记');
                        }
                        
                        console.log('当前上传的切片索引(H5):', options.chunkIndex);
                        
                        // 安全转换chunk数据为Blob
                        const bytes = safeStringToUint8Array(options.body.chunk, 'H5上传');
                        const blob = new Blob([bytes], { type: 'application/octet-stream' });
                        formData.append('chunk', blob, 'chunk-' + (options.chunkIndex || 0) + '.bin');
                        
                        // 使用fetch API上传
                        console.log('使用fetch API上传FormData');
                        const headers = getUploadHeaders();
                        
                        fetch('http://74ac341c.r3.cpolar.top/upload/files', {
                            method: 'POST',
                            headers: headers,
                            body: formData
                        })
                        .then(response => response.json())
                        .then(data => {
                            console.log('fetch上传成功');
                            resolve(data);
                        })
                        .catch(error => {
                            console.error('fetch上传失败:', error);
                            reject(error);
                        });
                    } catch (e) {
                        console.error('创建或发送FormData失败:', e);
                        reject(e);
                    }
                    return;
                }
                
                // 如果不支持FormData，使用uni.uploadFile
                if (typeof uni !== 'undefined' && uni.uploadFile) {
                    console.log('使用uni.uploadFile上传');
                    try {
                        console.log('当前上传的切片索引(uni):', options.chunkIndex);
                        
                        const uploadTask = uni.uploadFile({
                            url: 'http://74ac341c.r3.cpolar.top/upload/files',
                            filePath: filePath,
                            name: 'file',
                            header: getUploadHeaders(),
                            formData: {
                                fileId: fileId,
                                chunkIndex: String(options.chunkIndex || 0),
                                kbName: cleanString(options.kbName || ''),
                                kbId: options.kbId || '',
                                chunk: options.body.chunk
                            },
                            success: (uploadRes) => {
                                console.log('上传成功');
                                try {
                                    const data = JSON.parse(uploadRes.data);
                                    resolve(data);
                                } catch (parseError) {
                                    console.error('解析响应数据出错:', parseError);
                                    reject(parseError);
                                }
                            },
                            fail: (err) => {
                                console.error('上传失败:', err);
                                reject(err);
                            }
                        });
                        
                        uploadTask.onProgressUpdate((res) => {
                            console.log('上传进度', res.progress);
                        });
                    } catch (err) {
                        console.error('调用uni.uploadFile时出错:', err);
                        reject(err);
                    }
                } else {
                    reject(new Error('当前环境不支持文件上传'));
                }
            });
        }
        
        // 如果没有chunk数据，使用标准文件上传
        return new Promise((resolve, reject) => {
            if (typeof uni !== 'undefined' && uni.uploadFile) {
                try {
                    uni.uploadFile({
                        url: 'http://74ac341c.r3.cpolar.top/upload/files',
                        filePath: filePath,
                        name: 'file',
                        header: getUploadHeaders(),
                        formData: {
                            fileId: fileId,
                            kbName: cleanString(options.kbName || ''),
                            kbId: options.kbId || '',
                            chunk: options.body.chunk
                        },
                        success: (res) => {
                            console.log('文件上传成功');
                            try {
                                const data = JSON.parse(res.data);
                                resolve(data);
                            } catch (e) {
                                reject(e);
                            }
                        },
                        fail: (err) => {
                            console.error('文件上传失败:', err);
                            reject(err);
                        }
                    });
                } catch (e) {
                    console.error('uni.uploadFile调用失败:', e);
                    reject(e);
                }
            } else if (typeof wx !== 'undefined' && wx.uploadFile) {
                try {
                    wx.uploadFile({
                        url: 'http://74ac341c.r3.cpolar.top/upload/files',
                        filePath: filePath,
                        name: 'file',
                        header: getUploadHeaders(),
                        formData: {
                            fileId: fileId,
                            kbName: cleanString(options.kbName || ''),
                            kbId: options.kbId || '',
                            chunk: options.body.chunk
                        },
                        success: (res) => {
                            console.log('文件上传成功');
                            try {
                                const data = JSON.parse(res.data);
                                resolve(data);
                            } catch (e) {
                                reject(e);
                            }
                        },
                        fail: (err) => {
                            console.error('文件上传失败:', err);
                            reject(err);
                        }
                    });
                } catch (e) {
                    console.error('wx.uploadFile调用失败:', e);
                    reject(e);
                }
            } else {
                reject(new Error('当前环境不支持文件上传'));
            }
        });
    } catch (error) {
        console.error('上传过程中出错:', error);
        return Promise.reject(error);
    }
}; 